using System.Web.Security;
using System.Configuration.Provider;
using System.Collections.Specialized;
using System;
using System.Data.SqlClient;
using System.Configuration;
using System.Diagnostics;
using System.Web;
using System.Globalization;



namespace SubSonic.Web
{

    public sealed class SubSonicRoleProvider : RoleProvider
    {

        //
        // Global connection string, generic exception message, event log info.
        //

        //private string rolesTable = "Role";
        //private string usersInRolesTable = "UserRole";
        //private string userTable = "User";

        private string eventSource = "SubSonicRoleProvider";
        private string eventLog = "Application";
        private string exceptionMessage = "An exception occurred. Please check the Event Log.";

        private ConnectionStringSettings pConnectionStringSettings;
        private string connectionString;


        //
        // If false, exceptions are thrown to the caller. If true,
        // exceptions are written to the event log.
        //

        private bool pWriteExceptionsToEventLog = false;

        public bool WriteExceptionsToEventLog
        {
            get { return pWriteExceptionsToEventLog; }
            set { pWriteExceptionsToEventLog = value; }
        }



        //
        // System.Configuration.Provider.ProviderBase.Initialize Method
        //

        public override void Initialize(string name, NameValueCollection config)
        {

            //
            // Initialize values from web.config.
            //

            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "SubSonicRoleProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "SubSonic Role provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);


            if (config["applicationName"] == null || config["applicationName"].Trim() == "")
            {
                pApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
            }
            else
            {
                pApplicationName = config["applicationName"];
            }


            if (config["writeExceptionsToEventLog"] != null)
            {
                if (config["writeExceptionsToEventLog"].ToUpper() == "TRUE")
                {
                    pWriteExceptionsToEventLog = true;
                }
            }


            //
            // Initialize Connection.
            //

            pConnectionStringSettings = ConfigurationManager.
              ConnectionStrings[config["connectionStringName"]];

            if (pConnectionStringSettings == null || pConnectionStringSettings.ConnectionString.Trim() == "")
            {
                throw new ProviderException("Connection string cannot be blank.");
            }

            connectionString = pConnectionStringSettings.ConnectionString;
        }



        //
        // System.Web.Security.RoleProvider properties.
        //


        private string pApplicationName;


        public override string ApplicationName
        {
            get { return pApplicationName; }
            set { pApplicationName = value; }
        }

        //
        // System.Web.Security.RoleProvider methods.
        //

        //
        // RoleProvider.AddUsersToRoles
        //

        public override void AddUsersToRoles(string[] usernames, string[] rolenames)
        {
            foreach (string rolename in rolenames)
            {
                if (!RoleExists(rolename))
                {
                    throw new ProviderException("Role name not found.");
                }
            }

            foreach (string username in usernames)
            {
                if (username.IndexOf(',') > 0)
                {
                    throw new ArgumentException("User names cannot contain commas.");
                }

                foreach (string rolename in rolenames)
                {
                    if (IsUserInRole(username, rolename))
                    {
                        throw new ProviderException("User is already in role.");
                    }
                    else
                    {
                        AddUserToRole(username, rolename);
                    }
                }
            }
        }

        public void AddUserToRole(string username, string rolename)
        {
            LMSData.Role rl = new LMSData.Role();
            LMSData.User usr = new LMSData.User();
            LMSData.UserRoleMap usrl = new LMSData.UserRoleMap();
            rl.LoadByParam(LMSData.Role.Columns.RoleName, rolename);
            if (rl.IsLoaded)
            {
                usr.LoadByParam(LMSData.User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    usrl = new LMSData.UserRoleMap();
                    usrl.UserID = usr.UserID;
                    usrl.RoleID = rl.RoleID;
                    usrl.Save();
                }
            }
        }

        //
        // RoleProvider.CreateRole
        //

        public override void CreateRole(string rolename)
        {
            if (rolename.IndexOf(',') > 0)
            {
                throw new ArgumentException("Role names cannot contain commas.");
            }

            if (RoleExists(rolename))
            {
                throw new ProviderException("Role name already exists.");
            }

            LMSData.Role rl = new LMSData.Role();
            try
            {
                rl.RoleName = rolename;
                rl.Save();
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "CreateRole");
                }
                else
                {
                    throw e;
                }
            }

        }


        //
        // RoleProvider.DeleteRole
        //

        public override bool DeleteRole(string rolename, bool throwOnPopulatedRole)
        {
            SubSonic.Query qry = new Query(LMSData.UserRoleMap.Schema);
            
            if (!RoleExists(rolename))
            {
                throw new ProviderException("Role does not exist.");
            }

            if (throwOnPopulatedRole && GetUsersInRole(rolename).Length > 0)
            {
                throw new ProviderException("Cannot delete a populated role.");
            }

            LMSData.Role rl = new LMSData.Role();
            try
            {
                rl.LoadByParam(LMSData.Role.Columns.RoleName, rolename);
                if(rl.IsLoaded)
                {
                    // Delete everyone in Role
                    qry.AddWhere(LMSData.UserRoleMap.Columns.RoleID, rl.RoleID);
                    qry.QueryType = QueryType.Delete;
                    qry.Execute();
                    LMSData.Role.Destroy(rl.RoleID);
                }
            }
            catch (SqlException e)
            {
                
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "DeleteRole");

                    return false;
                }
                else
                {
                    throw e;
                }
            }

            return true;
        }


        ///
        /// Convert Collection to csv sting of role names
        /// 

        private string[] GetRolesFromCollection(LMSData.RoleCollection rlc)
        {
            string tmpRoleNames = "";

            foreach (LMSData.Role rl in rlc)
            {
                tmpRoleNames += rl.RoleName + ",";
            }

            if (tmpRoleNames.Length > 0)
            {
                // Remove trailing comma.
                tmpRoleNames = tmpRoleNames.Substring(0, tmpRoleNames.Length - 1);
                return tmpRoleNames.Split(',');
            }

            return new string[0];
        }

        ///
        /// Convert User Collection to csv sting of user names
        /// 

        private  string[] GetRolesFromCollection(LMSData.UserCollection usrs)
        {
            string tmpNames = "";

            foreach (LMSData.User usr in usrs)
            {
                tmpNames += usr.Username + ",";
            }

            if (tmpNames.Length > 0)
            {
                // Remove trailing comma.
                tmpNames = tmpNames.Substring(0, tmpNames.Length - 1);
                return tmpNames.Split(',');
            }

            return new string[0];
        }

        //
        // RoleProvider.GetAllRoles
        //

        public override string[] GetAllRoles()
        {

            LMSData.RoleCollection rlc = new LMSData.RoleCollection();
            string[] roles = new string[0];

            try
            {
                rlc.Load();
                roles = GetRolesFromCollection(rlc);
                
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllRoles");
                }
                else
                {
                    throw e;
                }
            }

            return roles;

            
        }

        //
        // RoleProvider.GetRolesForUser
        //

        public override string[] GetRolesForUser(string username)
        {

            LMSData.User usr = new LMSData.User();
            LMSData.RoleCollection rlc = null;

            try
            {
                usr.LoadByParam(LMSData.User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    rlc = usr.GetRoleCollection();
                    return GetRolesFromCollection(rlc);
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetRolesForUser");
                }
                else
                {
                    throw e;
                }
            }
            
            return new string[0];
        }


        //
        // RoleProvider.GetUsersInRole
        //

        public override string[] GetUsersInRole(string rolename)
        {
            LMSData.Role rl = new LMSData.Role();
            LMSData.UserCollection usrs = new LMSData.UserCollection();

            try
            {
                rl.LoadByParam(LMSData.Role.Columns.RoleName, rolename);
                if (rl.IsLoaded)
                {
                    usrs = rl.GetUserCollection();
                    return GetRolesFromCollection(usrs);
                }

            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUsersInRole");
                }
                else
                {
                    throw e;
                }
            }

            return new string[0];
        }


        //
        // RoleProvider.IsUserInRole
        //

        public override bool IsUserInRole(string username, string rolename)
        {
            bool userIsInRole = false;

            LMSData.User usr = new LMSData.User();
            LMSData.RoleCollection rlc = new LMSData.RoleCollection();

            try
            {
                usr.LoadByParam(LMSData.User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    rlc = usr.GetRoleCollection();
                    foreach (LMSData.Role rl in rlc)
                    {
                        if (rl.RoleName == rolename)
                            userIsInRole = true;
                    }
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "IsUserInRole");
                }
                else
                {
                    throw e;
                }
            }

            return userIsInRole;
        }


        //
        // RoleProvider.RemoveUsersFromRoles
        //

        public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames)
        {
            

            foreach (string username in usernames)
            {
                foreach (string rolename in rolenames)
                {
                    if (!IsUserInRole(username, rolename))
                    {
                        throw new ProviderException("User is not in role.");
                    }
                    else
                    {
                        RemoveUserFromRole(username, rolename);
                    }
                }
            }
        }

        public void RemoveUserFromRole(string username, string rolename)
        {
            LMSData.Role rl = new LMSData.Role();
            LMSData.User usr = new LMSData.User();
            LMSData.RoleCollection rlc = new LMSData.RoleCollection();

            try
            {
                rl.LoadByParam(LMSData.Role.Columns.RoleName, rolename);
                if (rl.IsLoaded)
                {
                    usr.LoadByParam(LMSData.User.Columns.Username, username);
                    if (usr.IsLoaded)
                    {
                        rlc = usr.GetRoleCollection();
                        foreach (LMSData.Role rll in rlc)
                        {
                            if (rll.RoleName == rl.RoleName)
                            {
                                rlc.Remove(rll);
                                break;
                            }
                            //rlc.Remove(rl);
                        }
                        LMSData.User.SaveRoleMap(usr.UserID, rlc);
                    }
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "RemoveUsersFromRole");
                }
                else
                {
                    throw e;
                }
            }
            
            
        }

        //
        // RoleProvider.RoleExists
        //

        public override bool RoleExists(string rolename)
        {
            bool exists = false;

            LMSData.Role rl = new LMSData.Role();

            try
            {
                rl.LoadByParam(LMSData.Role.Columns.RoleName, rolename);
                if (rl.IsLoaded)
                {
                    exists = true;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "RoleExists");
                }
                else
                {
                    throw e;
                }
            }

            return exists;
        }

        //
        // RoleProvider.FindUsersInRole
        //

        public override string[] FindUsersInRole(string rolename, string usernameToMatch)
        {


            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("LG_Role_FindUsersInRole", conn);
            cmd.Parameters.Add("@UsernameSearch", System.Data.SqlDbType.NVarChar, 255).Value = usernameToMatch;
            cmd.Parameters.Add("@RoleName", System.Data.SqlDbType.NVarChar,255).Value = rolename;
            

            string tmpUserNames = "";
            SqlDataReader reader = null;

            try
            {
                conn.Open();

                using(reader = cmd.ExecuteReader())
				{
					while (reader.Read())
					{
						tmpUserNames += reader.GetString(0) + ",";
					}
					reader.Close();
				}
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersInRole");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }

                conn.Close();
            }

            if (tmpUserNames.Length > 0)
            {
                // Remove trailing comma.
                tmpUserNames = tmpUserNames.Substring(0, tmpUserNames.Length - 1);
                return tmpUserNames.Split(',');
            }

            return new string[0];
        }

        //
        // WriteToEventLog
        //   A helper function that writes exception detail to the event log. Exceptions
        // are written to the event log as a security measure to avoid private database
        // details from being returned to the browser. If a method does not return a status
        // or boolean indicating the action succeeded or failed, a generic exception is also 
        // thrown by the caller.
        //

        private void WriteToEventLog(SqlException e, string action)
        {
            EventLog log = new EventLog();
            log.Source = eventSource;
            log.Log = eventLog;

            string message = exceptionMessage + "\n\n";
            message += "Action: " + action + "\n\n";
            message += "Exception: " + e.ToString();

            log.WriteEntry(message);
        }

        /*
         * private int GetRoleID(string rolename)
        {

            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("SELECT RoleID FROM [" + rolesTable + "] " +
                      "WHERE RoleName Rolename = @Rolename", conn);
            
            cmd.Parameters.Add("@RoleName", System.Data.SqlDbType.NVarChar, 255).Value = rolename;

            int roleid = 0;
            SqlDataReader reader = null;

            try
            {
                conn.Open();

                using(reader = cmd.ExecuteReader())
				{
					while (reader.Read())
					{
						roleid = reader.GetInt32(0);
					}
					reader.Close();
				}
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetRoleID");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }

                conn.Close();
            }

            return roleid;
        }
         * */

    }
}